package cn.stimd.spring.beans;

import org.springframework.util.ReflectionUtils;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 属性访问器的实现类，通过内省的方式访问对象的属性
 *
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWrapper{
    private BeanInfo beanInfo;

    public BeanWrapperImpl() {}

    public BeanWrapperImpl(Object object) {
        super(object);
    }

    private BeanWrapperImpl(Object object, BeanWrapperImpl parent) {
        super(object, parent);
    }


    private PropertyDescriptor[] getPropertyDescriptors() throws IntrospectionException {
        if(this.beanInfo == null){
            this.beanInfo = Introspector.getBeanInfo(getWrappedClass());
        }

        return this.beanInfo.getPropertyDescriptors();
    }


    @Override
    protected PropertyHandler getLocalPropertyHandler(String propertyName) {
        try {
            PropertyDescriptor pd = Arrays.stream(getPropertyDescriptors()).filter(x -> x.getName().equals(propertyName)).findFirst().orElse(null);
            if(pd == null){
                return null;
            }
            return new BeanPropertyHandler(pd);
        } catch (IntrospectionException e) {
            throw new BeansException("PropertyHandler not found", e);
        }
    }

    @Override
    protected AbstractPropertyAccessor newPropertyAccessor(Object object) {
        //传入parent主要是为了传递ConversionService
        return new BeanWrapperImpl(object, this);
    }


    public void setBeanInstance(Object object) {
        this.wrappedObject = object;
    }


    private class BeanPropertyHandler extends PropertyHandler {
        private final PropertyDescriptor descriptor;

        public BeanPropertyHandler(PropertyDescriptor descriptor) {
            super(descriptor.getPropertyType(), descriptor.getReadMethod() != null, descriptor.getWriteMethod() != null);
            this.descriptor = descriptor;
        }


        @Override
        public Object getValue() throws Exception {
            Method readMethod = this.descriptor.getReadMethod();
            readMethod.setAccessible(true);
            return readMethod.invoke(getWrappedInstance(), (Object[]) null);
        }

        @Override
        public void setValue(Object object, Object value) throws Exception {
            Method writeMethod = descriptor.getWriteMethod();
            ReflectionUtils.makeAccessible(writeMethod);
            writeMethod.invoke(getWrappedInstance(), value);
        }
    }
}
